/*
 * Copyright 2011 Clean GWT
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.cleangwt.uibinder.rebind;

import java.io.PrintWriter;

import com.cleangwt.uibinder.client.RequiresActionRebind;
import com.cleangwt.uibinder.client.RequiresFormRebind;
import com.cleangwt.uibinder.client.RequiresTemplate;
import com.cleangwt.uibinder.client.RequiresWebTemplate;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

/**
 * A generator for rebinding {@link RequiresActionRebind}s.
 * 
 * @author Jess
 * @version 1.0
 * @since 1.0
 */
public class RequiresRebindGenerator extends Generator {

  @Override
  public String generate(TreeLogger treeLogger, GeneratorContext context, String typeName)
      throws UnableToCompleteException {
    // this.logger = treeLogger;
    TypeOracle typeOracle = context.getTypeOracle();
    JClassType binderType = typeOracle.findType(typeName);
    String packageName = binderType.getPackage().getName();
    String className = binderType.getSimpleSourceName() + "Impl";
    PrintWriter printWriter = context.tryCreate(treeLogger, packageName, className);
    // print writer if null, source code has ALREADY been generated
    if (printWriter == null) {
      return packageName + "." + className;
    }
    // init composer, set class properties, create source writer
    ClassSourceFileComposerFactory composer =
        new ClassSourceFileComposerFactory(packageName, className);
    JClassType requriesTemplateType = typeOracle.findType(RequiresTemplate.class.getName());
    JClassType requriesWebTemplateType = typeOracle.findType(RequiresWebTemplate.class.getName());
    JClassType requriesActionRebindType = typeOracle.findType(RequiresActionRebind.class.getName());
    JClassType requriesFormRebindType = typeOracle.findType(RequiresFormRebind.class.getName());
    TemplateRebindWriter templateWriter = null;
    WebTemplateRebindWriter webTemplateWriter = null;
    ActionReindWriter actionRebindWriter = null;
    FormRebindWriter formRebindWriter = null;

    composer.setSuperclass(typeName);
    if (binderType.isAssignableTo(requriesTemplateType)) {
      if (binderType.isAssignableTo(requriesWebTemplateType)) {
        throw new IllegalArgumentException(
            "RequiresTemplate can not bound with RequiresWebTemplate.");
      }
      templateWriter = new TemplateRebindWriter(binderType);
      templateWriter.addImports(composer);

    }
    if (binderType.isAssignableTo(requriesWebTemplateType)) {
      webTemplateWriter = new WebTemplateRebindWriter(binderType);
      webTemplateWriter.addImports(composer);
    }
    if (binderType.isAssignableTo(requriesActionRebindType)) {
      actionRebindWriter = new ActionReindWriter(treeLogger, typeOracle, binderType);
    }
    if (binderType.isAssignableTo(requriesFormRebindType)) {
      formRebindWriter = new FormRebindWriter(treeLogger, typeOracle, binderType);
      formRebindWriter.addImports(composer);
    }
    SourceWriter sw = composer.createSourceWriter(context, printWriter);
    // create for possible sub binder types
    if (templateWriter != null) {
      sw.println();
      templateWriter.run(sw, className);
    }
    if (webTemplateWriter != null) {
      sw.println();
      webTemplateWriter.run(sw, className);
    }
    if (actionRebindWriter != null) {
      sw.println();
      actionRebindWriter.run(sw, className);
    }
    if (formRebindWriter != null) {
      sw.println();
      formRebindWriter.run(sw);
    }
    // end of class
    sw.outdent();
    sw.println("}");
    context.commit(treeLogger, printWriter);
    return packageName + "." + className;
  }

}
